package com.nlp.visualization.core.format;

import com.google.gson.GsonBuilder;
import com.nlp.visualization.common.CONSTANTS;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

import static com.nlp.visualization.common.CONSTANTS.TASK_MAX;


public class CSVUtil {

    private static Logger logger = LoggerFactory.getLogger(CSVUtil.class);

    private static ConcurrentLinkedQueue<Object> list;
//    //数据缓冲区
//    static StringBuilder str;

    /**
     * 读取爬虫数据的信息
     *
     * @param path
     * @return 微博实体对象的List
     */

    public static List read(String path) {

        List<CSVRecord> records = null;
        //读取csv的头
        String[] headers = CONSTANTS.WEIBO_CSV_HEADERS;
        //创建CSVFormat
        CSVFormat formator = CSVFormat.DEFAULT.withHeader(headers);

        //读取并转换
        try {
            FileReader fileReader = new FileReader(path);
            //创建CSVParser对象
            CSVParser parser = new CSVParser(fileReader, formator);
            records = parser.getRecords();
            parser.close();
            fileReader.close();
        } catch (IOException e) {

            logger.error("CSV对象读取出错");

        }


        return records;
    }

    /**
     * 多线程csv转json
     *
     * @param csvPath
     * @param jsonPath
     * @param csvHeaders
     * @param T
     */
    public static void csv2json(String csvPath, String jsonPath, String[] csvHeaders, Class<?> T) {

        //预处理csv文件
        preProcessCsv(csvPath);

        List<CSVRecord> records = null;
        //创建CSVFormat
        CSVFormat formator = CSVFormat.DEFAULT;

        //读取并转换
        try {
            //新建文件reader
            FileReader fileReader = new FileReader(csvPath);
            //创建CSVParser对象
            CSVParser parser = new CSVParser(fileReader, formator);
            records = parser.getRecords();
            //关闭输入
            parser.close();
            fileReader.close();
        } catch (IOException e) {
            logger.error("CSV对象读取出错");
            e.printStackTrace();
            return;
        }

        list = new ConcurrentLinkedQueue<>();
        //清空缓冲区的所有内容
//        str = new StringBuilder();
//        //增加一个[
//        str.append("[");
        // 创建线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        try {
            // 提交可分解的csv2JsonTask任务
            forkJoinPool.submit(new csv2JsonTask(1, records.size(), records, csvHeaders, T));
            forkJoinPool.shutdown();
            forkJoinPool.awaitTermination(CONSTANTS.TASK_TIMEOUT, TimeUnit.SECONDS);//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
        } catch (InterruptedException e) {
            logger.error("执行多线程转换的时候异常");
        }

        //将缓冲区写入给定路径中
        try {
//            str.append("]");
            File file = new File(jsonPath);
            file.delete();
            FileUtils.write(file, new GsonBuilder().setPrettyPrinting().create().toJson(list), "UTF-8", true);
        } catch (IOException e) {
            logger.error("写入JSON文件异常");
        } catch (Exception e) {
            logger.error("文件未知错误");
        }

    }


    /**
     * 对CSV文件进行预处理。去掉"\这样等字符
     *
     * @param csvPath
     */
    private static void preProcessCsv(String csvPath) {
        File file = new File(csvPath);
        try {
            List lines = FileUtils.readLines(file, "UTF-8");
            // 创建线程池
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            try {
                // 提交替换的任务
                forkJoinPool.submit(new RelaceTask(1, lines.size(), lines));
                forkJoinPool.shutdown();
                forkJoinPool.awaitTermination(CONSTANTS.TASK_TIMEOUT, TimeUnit.SECONDS);//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
            } catch (InterruptedException e) {
                logger.error("执行多线程替换的时候异常");
            }

            file.delete();
            //实现了逐句写入一个文件，参数boolean append为"true"时表示添加文件，而不是新建新的文件。
            FileUtils.writeLines(file, lines, false);

        } catch (IOException e) {
            logger.error("预处理csv文件出错");
            logger.error(e.getMessage());
        }

    }

    /**
     * csv转Json的任务类
     */
    static class csv2JsonTask extends RecursiveAction {
        // 每个"小任务"最多只50个数
        private static final int MAX = TASK_MAX;

        private int start;
        private int end;
        private List<CSVRecord> csvRecordList;
        private String[] csvHeaders;
        private Class T;

        csv2JsonTask(int start, int end, List<CSVRecord> csvRecordList, String[] csvHeaders, Class<?> C) {
            this.start = start;
            this.end = end;
            this.csvRecordList = csvRecordList;
            this.T = C;
            this.csvHeaders = csvHeaders;
        }

        @Override
        protected void compute() {
            // 当end-start的值小于MAX时候，开始执行任务
            if ((end - start) < MAX) {
                for (int i = start; i < end; i++) {
                    //开始执行任务，将每个CSV的对象转成一个JSON对象，并保存到缓冲区中
                    //实例化一个CLASS
                    Object o = null;
                    try {
                        o = T.newInstance();
                    } catch (InstantiationException e) {
                        logger.error("实例化类错误-");
                    } catch (IllegalAccessException e) {
                        logger.error("实例化类错误-非法访问权限");
                    }
                    //将CSV对象的值赋给JSON对象
                    int j = 0;
                    for (String s : csvHeaders) {
                        try {
                            //这里不能用getfield，因为这个是private的字段
                            Field f = T.getDeclaredField(s);
                            f.setAccessible(true);
                            f.set(o, csvRecordList.get(i).get(j));
                            j++;
                        } catch (NoSuchFieldException e) {
                            logger.error("转换csv对象的时候出错，没有这个数据域");
                        } catch (IllegalAccessException e) {
                            logger.error("转换csv对象的时候出错，非法的数据异常");
                        } catch (Exception e) {
                            logger.error("未知错误");
                        }
                    }
//                    //将JSON对象转成字符串，并加进缓冲区
//                    if (i == csvRecordList.size() - 1)
//                        str.append(new Gson().toJson(o) + "\r");
//                    else
//                        str.append(new Gson().toJson(o) + ",\r");
                    list.add(o);

                }
            } else {
                // 将大任务分解成两个小任务
                int middle = (start + end) / 2;
                csv2JsonTask left = new csv2JsonTask(start, middle, csvRecordList, csvHeaders, T);
                csv2JsonTask right = new csv2JsonTask(middle, end, csvRecordList, csvHeaders, T);
                // 并行执行两个小任务
                left.fork();
                right.fork();
            }
        }
    }


    /**
     * 预处理的任务类
     */
    static class RelaceTask extends RecursiveAction {
        // 每个"小任务"最多只50个数
        private static final int MAX = TASK_MAX;

        private int start;
        private int end;
        private List<String> lines;

        RelaceTask(int start, int end, List<String> lines) {
            this.start = start;
            this.end = end;
            this.lines = lines;
        }

        @Override
        protected void compute() {
            // 当end-start的值小于MAX时候，开始执行任务
            if ((end - start) < MAX) {
                for (int i = start; i < end; i++) {
                    lines.set(i, lines.get(i).replaceAll("\\\\\"", ""));
                }

            } else {
                // 将大任务分解成两个小任务
                int middle = (start + end) / 2;
                RelaceTask left = new RelaceTask(start, middle, lines);
                RelaceTask right = new RelaceTask(middle, end, lines);
                // 并行执行两个小任务
                left.fork();
                right.fork();
            }

        }

    }
}